﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml;
using PloobsEngine.Utils;
using PloobsEngine.Cameras;
using PloobsEngine.SceneControl;
using PloobsEngine.Light;
using PloobsEngine.Modelo;
using Microsoft.Xna.Framework.Graphics;

namespace PloobsEngine.Material
{
    internal class ImpostorDeferredMaterial : IMaterial
    {
        
        public enum Operation
        {
            NORMAL, IMPOSTOR
        }

        BilboardForward _shader;
        IShader _shaderNormal;
        MaterialType type;
        Operation op;
        float distanceToChangeToImpostor = 500;

        public float DistanceToChangeToImpostor
        {
            get { return distanceToChangeToImpostor; }
            set { distanceToChangeToImpostor = value; }
        }
        bool isFbxInvertedYZAxis = false;

        public ImpostorDeferredMaterial(IShader _shaderNormal,EngineStuff engine)
        {
            this.type = MaterialType.DEFERRED | MaterialType.FORWARD;            
            AffectedByShadow = true;
            this._shaderNormal = _shaderNormal;
            _shader = new BilboardForward();
            _shader.Initialize();
        }

        public ImpostorDeferredMaterial(IShader _shaderNormal, EngineStuff engine,bool isFbxInvertedYZAxis)
        {
            this.type = MaterialType.DEFERRED | MaterialType.FORWARD;
            AffectedByShadow = true;
            this._shaderNormal = _shaderNormal;
            _shader = new BilboardForward();
            _shader.Initialize();
            this.isFbxInvertedYZAxis = isFbxInvertedYZAxis;
        }

        /// <summary>
        /// Serialization
        /// </summary>
        public ImpostorDeferredMaterial()
        {
            AffectedByShadow = true;
        }

        #region IMaterial Members

        public void Drawn(GameTime gt, IObject obj, ICamera cam, IList<ILight> lights,IRenderHelper render)
        {            
            if(op == Operation.NORMAL)
                _shaderNormal.Draw(obj.Modelo,render,cam);
        }

        bool firstTime = true;
        public virtual void Update(GameTime gametime, IObject obj ,IList<ILight> lights)
        {
            if (firstTime)
            {
                ImpostorsBank.CreateImpostores(obj);
                firstTime = false;
            }

            _shader.Update(obj,lights);
            _shaderNormal.Update(obj, lights);
            if (obj.CameraDistance > distanceToChangeToImpostor)
            {
                op = Operation.IMPOSTOR;
            }
            else
            {
                op = Operation.NORMAL;
            }
        }

        public IShader Shadder
        {
            get
            {
                return _shaderNormal;
            }
            set
            {
                this._shaderNormal = value;
            }
        }        

        #endregion

        public MaterialType MaterialType
        {
            get
            {
                return type;
            }
            set
            {
                this.type = value;
            }
        }


        #region IMaterial Members

        public virtual void Serialize(System.Xml.XmlTextWriter textWriter)
        {
        }

        public virtual void DeSerialize(System.Xml.XmlNode node,EngineStuff engine, IModelo mode)
        {
        }

        #endregion

        #region IMaterial Members


        public void PreDrawnPhase(IWorld mundo,GameTime gt, IObject obj, ICamera cam, IList<ILight> lights, IRenderHelper render)
        {
            _shader.PreDrawPhase(mundo,obj.Modelo,render,cam);
        }

        #endregion

        #region IMaterial Members


        public bool AffectedByShadow
        {
            set;
            get;
        }

        #endregion

        #region IMaterial Members


        public bool isThisMaterialType(MaterialType type)
        {
            if( (type & this.type) == type)
                return true;
            return false;                
        }

        #endregion

        #region IMaterial Members        

        public void PosDrawnPhase(GameTime gt, IObject obj, ICamera cam, IList<ILight> lights, IRenderHelper render)
        {
            if (op == Operation.IMPOSTOR)
            {
                // Obtener el vector hacia delante de la cámara
                Vector3 camforward = Matrix.CreateFromQuaternion(cam.Rotation).Forward; ///Arrumar, ta imbecil overkill isso
                // No vamos a emplear la componente Y, poner a 0 y normalizar
                camforward.Y = 0.0f;
                camforward.Normalize();
                /* Mediante la arcotangente obtenemos la rotación en el eje Y de la cámara. Sin embargo, como
                 * los personajes están mirando en la dirección positiva del eje Z, tenemos que añadirle 180º. */
                float angle = MathHelper.ToDegrees((float)Math.Atan2(camforward.X, camforward.Z)) + 180.0f;

                /* Como el impostor a dibujar depende de la orientación de la cámara, dividimos el ángulo en tramos
                 * de 45º (y añadimos 22.5º de offset para que funcione correctamente), y a partir de ahi 
                 * obtendremos el índice de la textura a usar. */
                int impostorActivo = 8 - ((int)(angle + 22.5f) / 45);
                if (impostorActivo > 7)
                    impostorActivo = 0;

                _shader.Texture = ImpostorsBank.ImpostoresDictionary[obj.Modelo.Name][impostorActivo];
                if (isFbxInvertedYZAxis)
                {
                    _shader.Position = new Vector3(obj.Position.X,obj.Position.Z, obj.Position.Y);
                }
                else
                {
                    _shader.Position = obj.Position;
                }
                _shader.Draw(obj.Modelo, render, cam);
            }                        
        }

        #endregion

        #region IMaterial Members


        public IDepthExtractor DepthExtractor
        {
            get { return null; }
        }

        #endregion
    }
}
